//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<SceneManager.cpp>
///	@path	~/src/scene/
///	@date	2007/10/17
///	@desc	Scene Manager.

#include "config/config.h"

#include "lib/utilities/debug.h"
#include "lib/utilities/observer.h"
#include "lib/system/thread_debug.h"

#include "scene/SceneManager.h"
#include "scene/lives/CStaticLive.h"

namespace xeres {

	// ctor
	SceneManager::SceneManager( void )
		: m_active( NULL )
		, m_inited( false )
		, m_loader( NULL )
	{
		// register most basic lives & entities
		IOBJECT_REGISTER_CLASS( CStaticLive );
	}

	// dtor
	SceneManager::~SceneManager( void )
	{
	}

	// Initialize
	void SceneManager::Initialize( ResourceLoader * loader , RefWeak<IData> config )
	{
		TRACK_FUNCTION_SCOPE();

		if( m_inited )
		{
			TRACE_ERROR( _S("SceneManager::Initialize: Scene manager is initialized.") );
			return;
		}

		m_loader = loader;

		if( config.IsValid() )
		{
			// load scene settings from config
			std::vector< Ref<IData> > scenes;
			config->OpenDataArray( _S("scene") , scenes );

			for( size_t i = 0 , total = scenes.size() ; i < total ; ++i )
			{
				_SceneNote note;
				if( !scenes[i]->GetAttribute( _S("class") , note.className ) )
				{
					TRACE_ERROR( _S("SceneManager::Initialize: Corrupted definition, scene has not defined class name.") );
					continue;
				}
				// set path
				if( ! scenes[i]->GetAttribute( _S("resource") , note.path ) )
				{
					TRACE_ERROR( _S("SceneManager::Initialize: Corrupted definition, scene has not defined resource path.") );
					continue;
				}

				WString name;
				if( !scenes[i]->GetAttribute( _S("name") , name ) )
				{
					TRACE_ERROR( _S("SceneManager::Initialize: Corrupted definition, scene name has note defined.") );
					continue;
				}

				// check duplicated name
				if( m_scenes.find( name ) != m_scenes.end() )
				{
					TRACE_ERROR( _S("SceneManager::Initialize: Duplicated scene name '%s' defined.") , name.c_str() );
					continue;
				}

				TRACE_INFO( _S("Found scene <%s>.") , name.c_str() );

				// record scene note
				m_scenes[ name ] = note;
			}
		}

		m_inited = true;
	}

	// Finalize
	void SceneManager::Finalize( void )
	{
		TRACK_FUNCTION_SCOPE();

		if( !m_inited )
		{
			TRACE_ERROR( _S("SceneManager::Finalize: Scene manager is not initialized.") );
			return;
		}

		m_active = NULL;
		m_scenes.clear();
		m_inited = false;
	}

	// update scene
	void SceneManager::Tick( const Frame& frame )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("tick|!_render") );

		COUNTER_GUARD( _S("scene") );

		if( m_active.IsValid() )
		{
			m_active->Tick( frame );
		}
	}

	// present scene
	void SceneManager::Present( RenderManager& rm )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("render|!_tick") );

		BeginPresent();

		if( m_active.IsValid() )
		{
			m_active->Present( *this );
		}

		EndPresent( rm );

		if( m_camera.IsValid() )
		{
			rm.GetMainPass()->SetPassView( m_camera->GetCameraView() );
		}
	}

	// LoadScene
	bool SceneManager::PreLoadScene( const WString& name )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|pool*|!_render") );

		// find note
		if( m_scenes.has_key( name ) == false )
		{
			TRACE_ERROR( _S("SceneManager::LoadScene: Acquire scene '%s' is not found in scene configuration.") ,
				name.c_str() );
			return false;
		}

		_SceneNote& sn = m_scenes[ name ];

		// Guard locker without lock.
		LockGuardWrap<SpinLock> locker( sn.lock , false );

		// Get scene lock.
		if( !locker.TryAcquire() )
		{
			TRACE_ERROR( _S("SceneManager::LoadScene: Another operation is applying on scene '%s'.") ,
				name.c_str() );
			return false;
		}

		// Scene has been already loaded.
		if( sn.scene.IsValid() )
			return true;

		// Open scene def.
		Ref<IData> def = m_loader->OpenData( sn.path );
		if( def.IsNull() )
		{
			TRACE_ERROR( _S("SceneManager::LoadScene: Scene '%s' does not exist in defined path.") ,
				name.c_str() );
			return false;
		}

		// Create new class.
		sn.scene = ObjectFactory::CreateObject( sn.className );
		if( !sn.scene->Initialize( name , m_loader , def ) )
		{
			sn.scene = NULL;
			return false;
		}
		return true;
	}
	// ActiveScene
	bool SceneManager::ActiveScene( const WString& name )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|pool*|!_render") );

		// find note
		if( m_scenes.has_key( name ) == false )
		{
			TRACE_ERROR( _S("SceneManager::ActiveScene: Acquire scene '%s' is not found in scene configuration.") ,
				name.c_str() );
			return false;
		}

		_SceneNote& sn = m_scenes[ name ];

		// Guard locker without lock.
		LockGuardWrap<SpinLock> locker( sn.lock , false );

		// Get scene lock.
		if( !locker.TryAcquire() )
		{
			TRACE_ERROR( _S("SceneManager::ActiveScene: Another operation is applying on scene '%s'.") ,
				name.c_str() );
			return false;
		}

		// Scene has been already loaded.
		if( sn.scene.IsNull() )
		{
			TRACE_ERROR( _S("SceneManager::ActiveScene: Scene '%s' is not loaded for active.") , name.c_str() );
			return false;
		}

		// leave actived scene
		if( m_active.IsValid() )
		{
			m_active->Leave();
			m_active = NULL;
		}

		// enter new scene
		m_active = sn.scene;
		m_active->Enter( m_camera );

		return true;
	}
	// SetCamera
	void SceneManager::SetCamera( RefWeak<ICamera> cam )
	{
		XS_ASSERT( cam.IsValid() );

		m_camera = cam;
		if( m_active.IsValid() )
			m_active->Enter( cam );
	}

	// GetCamera
	RefWeak<ICamera> SceneManager::GetCamera( void )
	{
		return m_camera;
	}

} // namespace xeres
